/**
 * Copyright (c) 2010 XXXXX
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ar.edu.itba.ia2010.geneticos.motor.api.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import ar.edu.itba.ia2010.geneticos.motor.api.Cromosoma;
import ar.edu.itba.ia2010.geneticos.motor.api.Gen;

/**
 * {@link Cromosoma} que guarda sus genes en una {@link List}.
 * Inmutable.
 * 
 * @author Juan F. Codagnone
 * @since Mar 21, 2010
 * @param <T> tipo del gen
 */
public class ListCromosoma<T> extends AbstractCromosoma<T> {
    private final List<Gen<T>> genes;
    private final List<Gen<T>> unmodificableGenes; 
    
    /** Creates the BooleanBitCromosoma. */
    public ListCromosoma(final List<Gen<T>> genes) {
        Validate.notNull(genes);
        this.genes = genes;
        this.unmodificableGenes = Collections.unmodifiableList(genes);
    }
    
    /** @see Iterable#iterator() */
    public final Iterator<Gen<T>> iterator() {
        return unmodificableGenes.iterator();
    }

    /** @see Cromosoma#listiterator() */
    public final ListIterator<Gen<T>> listiterator() {
        return new OnlySetListIterator<Gen<T>>(genes.listIterator());
    }

    /** @see Cromosoma#crossover(Random, Cromosoma) */
    @SuppressWarnings("unchecked")
    public final Cromosoma<T>[] crossover(final Random random, 
            final Cromosoma<T> pareja) {
        Validate.notNull(random);
        Validate.notNull(pareja);
        
        final int ptoCruce = Double.valueOf(Math.floor(random.nextDouble() 
                * (genes.size() - 1))).intValue();
        if(pareja instanceof ListCromosoma) {
            final ListCromosoma<T> p = (ListCromosoma<T>) pareja;
            
            final int size = genes.size();
            Validate.isTrue(size == p.genes.size(), 
                    "no tiene sentido cruzar cromosomas de diferentes largos "
                    + size + " vs " +  p.genes.size());
            final int i = ptoCruce + 1;
            
            final Cromosoma<T>[] ret = new Cromosoma[2];
            final List<Gen<T>> h1 = new ArrayList<Gen<T>>(size);
            final List<Gen<T>> h2 = new ArrayList<Gen<T>>(size);
            h1.addAll(genes.subList(0, i));
            h2.addAll(p.genes.subList(0, i));
            
            h1.addAll(p.genes.subList(i, size));
            h2.addAll(genes.subList(i , size));
            
            ret[0] = new ListCromosoma<T>(h1);
            ret[1] = new ListCromosoma<T>(h2);
            return ret;
        } else {
            throw new IllegalArgumentException(
                "no tiene sentido cruzar cromosomas de diferentes tipos!: debiera "
                + "ser " + getClass().getName() + " pero fue "
                + pareja.getClass().getName());
        }
    }
    
    /** @see Object#toString() */
    @Override
    public final String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                    .append(genes)
                    .toString();
    }
    
    /** @see Object#equals(Object) */
    @Override
    public final boolean equals(final Object obj) {
        boolean ret = false;
        
        if(obj instanceof ListCromosoma<?>) {
            final ListCromosoma<?> l = (ListCromosoma<?>) obj;
            ret = genes.equals(l.genes);
        }
        
        return ret;
    }
    
    /** @see Object#hashCode() */
    @Override
    public final int hashCode() {
        return genes.hashCode();
    }
}
